import 'dart:convert';
import 'package:grpc/grpc.dart';
import 'package:grpc/src/client/http2_connection.dart';
import 'package:member_center_sdk/grpc/mx_stream.pb.dart';
import 'package:member_center_sdk/grpc_impl/grpc_result.dart';

import 'package:member_center_sdk/grpc_impl/base_client_channel.dart';
import 'package:protobuf/protobuf.dart';
import 'package:uuid/uuid.dart';

typedef ResponseFuture<R> RequestMethod<Q extends GeneratedMessage,
    R extends GeneratedMessage>(Q request, {CallOptions options});

typedef ClientChannel InitChannelMethod();

class ClientException implements Exception {
  final String message;
  final int code;

  const ClientException(this.message, {this.code});
}

class BaseClient {
  static const int timeout = 20;

  String host;
  int port;
  InitChannelMethod initChannelMethod;
  BaseClientChannel _channel;
  Map<String, int> tryRequests = {};

  BaseClient(this.host, this.port, this.initChannelMethod) {
    _init();
  }

  _init() {
    _channel = initChannelMethod();
  }

  Future<R> _request<Q extends GeneratedMessage, R extends GeneratedMessage>(
      ResponseFuture<R> requestHandler(CallOptions options),
      {CallOptions options}) async {
    String requestId;
    if (options != null &&
        options.metadata != null &&
        options.metadata.containsKey("requestId")) {
      requestId = options.metadata["requestId"];
      if (tryRequests.containsKey(requestId)) {
        if (tryRequests[requestId] > 3) {
          //重试4次
          throw new ClientException("网络请求错误,请重试");
        }
      }
    }
    requestId ??= Uuid().v4();
    var beginTime = DateTime.now();
    Http2ClientConnection connection;
    await Future.doWhile(() async {
      try {
        var timespan = DateTime.now().difference(beginTime).inSeconds;
        if (timespan > timeout) {
          return false;
        }
        connection = await _channel.getConnection();
        print("间隔:${timespan},状态:${connection.state}");
        if (connection.state == ConnectionState.connecting) {
          return new Future.delayed(new Duration(seconds: 1), () {
            return true;
          });
        } else if (connection.state == ConnectionState.transientFailure ||
            connection.state == ConnectionState.shutdown) {
          _init();
          return new Future.delayed(new Duration(seconds: 1), () {
            return true;
          });
        } else {
          return false;
        }
      } catch (ex) {
        return new Future.delayed(new Duration(seconds: 1), () {
          return true;
        });
      }
    });
    if (connection.state != ConnectionState.ready &&
        connection.state != ConnectionState.idle) {
      throw new ClientException("网络请求错误,请重试");
    } else {
      if (options == null) options = CallOptions();
      var newOptions = options.mergedWith(CallOptions(
        timeout: Duration(seconds: timeout),
      ));
      newOptions = newOptions.mergedWith(CallOptions(
        metadata: {"requestId": requestId},
      ));
      try {
        print("开始请求:${DateTime.now()}");
        ResponseFuture<R> future = requestHandler(newOptions);
        var r = await future;
        print("结束请求:${DateTime.now()}");
        return r;
      } on GrpcError catch (ex) {
        if (ex.code == StatusCode.unavailable ||
            ex.code == StatusCode.aborted) {
          _init();
          var tryCount =
              tryRequests.containsKey(requestId) ? tryRequests[requestId] : 0;
          tryRequests[requestId] = tryCount + 1;
          return await this._request(requestHandler, options: newOptions);
        } else {
          var cxt = _channel.context(requestId);
          if (cxt.clientCall.isCancelled) {
            throw new ClientException("网络请求错误,请重试");
          } else {
            var trailers = await cxt.clientCall.trailers;
            if (trailers.containsKey("error")) {
              var error = trailers["error"];
              var errBytes = base64.decode(error);
              var errMsg = ErrorMessage.fromBuffer(errBytes);
              throw new ClientException(errMsg.message, code: errMsg.code);
            } else {
              throw new ClientException("网络请求错误,请重试");
            }
          }
        }
      } catch (ex) {
        throw new ClientException("网络请求错误,请重试");
      }
    }
  }

  Future<GrpcResult<R>>
      doAction<Q extends GeneratedMessage, R extends GeneratedMessage>(
          ResponseFuture<R> requestHandler(CallOptions options)) async {
    try {
      var result = await _request<Q, R>(requestHandler);
      var grpcResult = new GrpcResult<R>();
      grpcResult.success = true;
      grpcResult.result = result;
      return grpcResult;
    } on ClientException catch (ex) {
      return GrpcResult<R>()
        ..success = false
        ..code = ex.code
        ..message = ex.message;
    }
  }
}
